use crate::bitboard::BitBoard;
use crate::color::Color;
use crate::color::COLORS;
use crate::piece::Piece;
use crate::piece::PieceType;
use crate::piece::PIECE_TYPES;
use std::convert::TryInto;
use std::num::NonZeroU8;
use std::ops::Index;
use std::ops::IndexMut;

#[derive(Clone, Copy, PartialEq, Eq)]
pub struct Pos {
    // 因为要使用NonZero，这个值是+1处理过的
    p_add_1: NonZeroU8,
}

pub enum Dir {
    Up = -1,
    Down = 1,
    Left = -10,
    Right = 10,
}

impl Pos {
    pub fn new() -> Pos {
        Pos {
            p_add_1: NonZeroU8::new(1).unwrap(),
        }
    }
    fn build_pos(row: i32, column: i32) -> Pos {
        assert!((0..10).contains(&row));
        assert!((0..9).contains(&column));
        let p: u32 = (row + column * 10).try_into().unwrap();
        let p: u8 = (p + 1).try_into().unwrap();
        assert!(p != 0);
        Pos {
            p_add_1: unsafe { NonZeroU8::new_unchecked(p) },
        }
    }
    pub fn from_pos_index(index: usize) -> Pos {
        assert!(index < 90);
        Pos {
            p_add_1: NonZeroU8::new((index + 1).try_into().unwrap()).unwrap(),
        }
    }
    pub fn row(self) -> usize {
        ((self.p_add_1.get() - 1) % 10).into()
    }
    pub fn column(self) -> usize {
        ((self.p_add_1.get() - 1) / 10).into()
    }
    fn pos_side(self) -> Color {
        if self.row() <= 4 {
            Color::BLACK
        } else {
            Color::RED
        }
    }
    pub fn pos_index(self) -> usize {
        (self.p_add_1.get() - 1).into()
    }
    pub fn relative(self, dir: Dir) -> Option<Pos> {
        let mut row: i32 = self.row().try_into().unwrap();
        let mut column: i32 = self.column().try_into().unwrap();
        match dir {
            Dir::Up => row -= 1,
            Dir::Down => row += 1,
            Dir::Left => column -= 1,
            Dir::Right => column += 1,
        }
        if 0 <= row && row < 10 && 0 <= column && column < 9 {
            Some(Pos::build_pos(row, column))
        } else {
            None
        }
    }
}

#[derive(Clone)]
struct BoardPieceArray {
    array: [Option<Piece>; 10 * 9],
}

struct BoardBitBoardInnerArray {
    array: [BitBoard; 7],
}

struct BoardBitBoardArray {
    array: [BoardBitBoardInnerArray; 2],
}

struct KingPosArray {
    array: [Pos; 2],
}

impl BoardPieceArray {
    fn count_piece(&self, piece: Piece) -> u32 {
        let mut count = 0;
        for i in self.array.iter() {
            if let Some(t) = i {
                if *t == piece {
                    count += 1;
                }
            }
        }
        count
    }
}

struct BitBoardBySide {
    bitboard_array: [BitBoard; 2],
}

impl Index<Color> for BitBoardBySide {
    type Output = BitBoard;

    fn index(&self, color: Color) -> &BitBoard {
        &self.bitboard_array[color.color_index()]
    }
}

impl IndexMut<Color> for BitBoardBySide {
    fn index_mut(&mut self, color: Color) -> &mut BitBoard {
        &mut self.bitboard_array[color.color_index()]
    }
}

pub struct Board {
    piece_array: BoardPieceArray,
    side_to_move: Color,
    bitboard_array: BoardBitBoardArray,
    king_pos: KingPosArray,
    piece_bitboard_by_side: BitBoardBySide,
}

impl Index<Pos> for BoardPieceArray {
    type Output = Option<Piece>;

    fn index(&self, pos: Pos) -> &Self::Output {
        let pos: usize = (pos.p_add_1.get() - 1).into();
        &self.array[pos]
    }
}

impl IndexMut<Pos> for BoardPieceArray {
    fn index_mut(&mut self, pos: Pos) -> &mut Self::Output {
        let pos: usize = (pos.p_add_1.get() - 1).into();
        &mut self.array[pos]
    }
}

impl Index<Color> for BoardBitBoardArray {
    type Output = BoardBitBoardInnerArray;

    fn index(&self, color: Color) -> &Self::Output {
        &self.array[color.color_index()]
    }
}

impl IndexMut<Color> for BoardBitBoardArray {
    fn index_mut(&mut self, color: Color) -> &mut Self::Output {
        &mut self.array[color.color_index()]
    }
}

impl Index<PieceType> for BoardBitBoardInnerArray {
    type Output = BitBoard;

    fn index(&self, piece_type: PieceType) -> &Self::Output {
        let index: usize = piece_type.piece_type_index();
        &self.array[index]
    }
}

impl IndexMut<PieceType> for BoardBitBoardInnerArray {
    fn index_mut(&mut self, piece_type: PieceType) -> &mut Self::Output {
        let index: usize = piece_type.piece_type_index();
        &mut self.array[index]
    }
}

impl Index<Color> for KingPosArray {
    type Output = Pos;

    fn index(&self, color: Color) -> &Self::Output {
        &self.array[color.color_index()]
    }
}

impl IndexMut<Color> for KingPosArray {
    fn index_mut(&mut self, color: Color) -> &mut Self::Output {
        &mut self.array[color.color_index()]
    }
}

pub fn parse_fen_str(fen_str: &str) -> Result<Board, String> {
    if !fen_str.is_ascii() {
        return Err("FEN串含有不能识别的字符".to_string());
    }
    // 按照空格或者TAB分隔
    fn is_space_or_tab(c: char) -> bool {
        c == ' ' || c == '\t'
    }
    let fen_str_split: Vec<&str> = fen_str
        .split(is_space_or_tab)
        .filter(|s| !s.is_empty())
        .collect();
    if fen_str_split.len() < 2 {
        return Err("没有找到空白字符分隔符".to_string());
    }
    let fen_str: &str = fen_str_split[0];
    let color: &str = fen_str_split[1];

    fn replace_digits(line: &[u8]) -> Result<Box<[u8]>, String> {
        // 展开FEN串中的数字，转变为等同于数字数量的空格
        let mut v: Vec<u8> = Vec::new();
        let mut prev_one_is_digit = false;
        for &c in line {
            let ch: char = c.into();
            if ch.is_digit(10) {
                if prev_one_is_digit {
                    // 出现连续的数字
                    return Err("不知道如何解释连续数字".to_string());
                }
                let i: i32 = ch.to_string().parse().unwrap();
                if i == 0 {
                    return Err("不知道如何解释数字0".to_string());
                }
                for _ in 1..=i {
                    v.push(b' ');
                }
                prev_one_is_digit = true;
            } else {
                v.push(c);
                prev_one_is_digit = false;
            }
        }
        if v.len() != 9 {
            return Err("至少有一行棋子数量不正确".to_string());
        }
        Ok(v.into())
    }
    fn map_piece(line: &[u8]) -> Result<Box<[Option<Piece>]>, String> {
        let mut v: Vec<Option<Piece>> = Vec::new();
        for &i in line {
            if i == b' ' {
                v.push(None);
            } else if let Some(p) = Piece::parse_char(i.into()) {
                v.push(Some(p));
            } else {
                let mut err_msg = "无法解析字符".to_string();
                err_msg.push(i.into());
                return Err(err_msg);
            }
        }
        Ok(v.into())
    }
    fn parse_fen_part(str: &str) -> Result<BoardPieceArray, String> {
        // 解析棋盘上的子力位置信息
        let str = str.as_bytes();
        let v: Vec<&[u8]> = str.split(|&c| c == b'/').collect();
        if v.len() != 10 {
            return Err("棋盘行数不正确".to_string());
        }
        if v.iter().any(|x| x.is_empty()) {
            return Err("棋盘有空行".to_string());
        }
        let v: Vec<Result<_, String>> = v.iter().map(|&x| replace_digits(x)).collect();
        if v.iter().any(Result::is_err) {
            // 如果有至少一行解析出错，就返回错误
            let r: &Result<_, String> = v.iter().find(|&x| x.is_err()).unwrap();
            return Err(r.clone().err().unwrap());
        }
        let v: Vec<Box<[u8]>> = v.iter().map(|r| r.clone().ok().unwrap()).collect();
        let v: Vec<Result<_, String>> = v.iter().map(|x| map_piece(x)).collect();
        if v.iter().any(Result::is_err) {
            // 如果有至少一行解析出错，就返回错误
            let r: &Result<_, String> = v.iter().find(|&x| x.is_err()).unwrap();
            return Err(r.clone().err().unwrap());
        }
        let v: Vec<Box<[Option<Piece>]>> = v.iter().map(|r| r.clone().ok().unwrap()).collect();
        let mut board_piece_array = BoardPieceArray { array: [None; 90] };
        for (row, line) in v.iter().enumerate() {
            for column in 0..line.len() {
                let p: Option<Piece> = line[column];
                let pos = Pos::build_pos(row.try_into().unwrap(), column.try_into().unwrap());
                board_piece_array[pos] = p;
            }
        }
        Ok(board_piece_array)
    }
    fn parse_color_part(str: &str) -> Result<Color, String> {
        // 解析轮到哪方走子
        let str = str.to_ascii_lowercase();
        if str == "w" || str == "r" {
            Ok(Color::RED)
        } else if str == "b" {
            Ok(Color::BLACK)
        } else {
            Err("无法解析轮到哪方走子".to_string())
        }
    }
    fn check_piece_number(
        piece_array: &BoardPieceArray,
        piece_type: PieceType,
        num_constraint: Box<dyn Fn(u32) -> bool>,
    ) -> Result<(), String> {
        // 检查棋子数量的辅助函数
        for &color in COLORS.iter() {
            let piece = Piece::new(color, piece_type);
            if !num_constraint(piece_array.count_piece(piece)) {
                let piece_type_string = piece_type.human_string(color);
                return Err(piece_type_string + "的数量不正确");
            }
        }
        Ok(())
    }
    fn check_no_cross_river(piece_array: &BoardPieceArray) -> Result<(), String> {
        // 检查是否有违规“过河”的棋子
        let pieces = [PieceType::KING, PieceType::GUARD, PieceType::MINISTER];
        for (i, p) in piece_array.array.iter().enumerate() {
            if let Some(p) = p {
                if pieces.contains(&p.piece_type()) {
                    let pos = Pos::from_pos_index(i);
                    if p.color() != pos.pos_side() {
                        let piece_type_string = p.piece_type().human_string(p.color());
                        return Err(piece_type_string + "违规过河");
                    }
                }
            }
        }
        Ok(())
    }
    // 检查兵卒的位置
    fn check_pawn_init_pos(bitboard: &BoardBitBoardArray) -> Result<(), String> {
        for &color in COLORS.iter() {
            let piece_type_string = PieceType::PAWN.human_string(color);
            let bitboard = bitboard[color][PieceType::PAWN];
            if (bitboard & !BitBoard::pawn_pos_bitboard(color)).is_not_empty() {
                return Err(piece_type_string + "的位置不正确");
            }
            let mut same_side_column_count = [0; 9];
            for pos in bitboard {
                if pos.pos_side() == color {
                    let column: usize = pos.column();
                    same_side_column_count[column] += 1;
                }
            }
            if same_side_column_count.iter().any(|x| *x > 1) {
                return Err(piece_type_string + "的位置不正确");
            }
        }
        Ok(())
    }
    fn check_king_minister_guard_pos(bitboard: &BoardBitBoardArray) -> Result<(), String> {
        for &color in COLORS.iter() {
            for &(piece_type, allowed) in [
                (PieceType::KING, BitBoard::king_pos_bitboard()),
                (PieceType::MINISTER, BitBoard::minister_pos_bitboard()),
                (PieceType::GUARD, BitBoard::guard_pos_bitboard()),
            ]
            .iter()
            {
                if (bitboard[color][piece_type] & !allowed).is_not_empty() {
                    let piece_type_string = piece_type.human_string(color);
                    return Err(piece_type_string + "的位置不正确");
                }
            }
        }
        Ok(())
    }
    fn init_bitboard_array(piece_array: &BoardPieceArray) -> BoardBitBoardArray {
        // 初始化位棋盘
        let mut board_bitboard_array = BoardBitBoardArray {
            array: [
                BoardBitBoardInnerArray {
                    array: [BitBoard::new(); 7],
                },
                BoardBitBoardInnerArray {
                    array: [BitBoard::new(); 7],
                },
            ],
        };
        for &color in COLORS.iter() {
            for &piece_type in PIECE_TYPES.iter() {
                let piece = Piece::new(color, piece_type);
                let bitboard = BitBoard::init_from_piece_array_pred(
                    &piece_array.array,
                    Box::new(move |p| p == piece),
                );
                board_bitboard_array[color][piece_type] = bitboard;
            }
        }
        board_bitboard_array
    }
    fn init_king_pos(piece_array: &BoardPieceArray) -> KingPosArray {
        let mut king_pos_array = KingPosArray {
            array: [Pos::new(); 2],
        };
        for (i, p) in piece_array.array.iter().enumerate() {
            if let Some(p) = p {
                if p.piece_type() == PieceType::KING {
                    let color_index: usize = p.color().color_index();
                    king_pos_array.array[color_index] = Pos::from_pos_index(i);
                }
            }
        }
        king_pos_array
    }
    let piece_array: BoardPieceArray = parse_fen_part(fen_str)?;
    // 检查棋子数量
    check_piece_number(&piece_array, PieceType::KING, Box::new(|x| x == 1))?;
    check_piece_number(&piece_array, PieceType::CHARIOT, Box::new(|x| x <= 2))?;
    check_piece_number(&piece_array, PieceType::CANNON, Box::new(|x| x <= 2))?;
    check_piece_number(&piece_array, PieceType::KNIGHT, Box::new(|x| x <= 2))?;
    check_piece_number(&piece_array, PieceType::MINISTER, Box::new(|x| x <= 2))?;
    check_piece_number(&piece_array, PieceType::GUARD, Box::new(|x| x <= 2))?;
    check_piece_number(&piece_array, PieceType::PAWN, Box::new(|x| x <= 5))?;
    // 检查是否有违规“过河”的棋子
    check_no_cross_river(&piece_array)?;
    // 初始化位棋盘
    let bitboard_array = init_bitboard_array(&piece_array);
    // 检查兵卒的位置
    check_pawn_init_pos(&bitboard_array)?;
    // 检查帅将象士的位置
    check_king_minister_guard_pos(&bitboard_array)?;
    let king_pos = init_king_pos(&piece_array);
    Ok(Board {
        piece_array: piece_array.clone(),
        side_to_move: parse_color_part(color)?,
        bitboard_array,
        king_pos,
        piece_bitboard_by_side: BitBoardBySide {
            bitboard_array: [
                BitBoard::init_from_piece_array_pred(
                    &piece_array.array,
                    Box::new(|p| p.color() == Color::BLACK),
                ),
                BitBoard::init_from_piece_array_pred(
                    &piece_array.array,
                    Box::new(|p| p.color() == Color::RED),
                ),
            ],
        },
    })
}

impl Index<Pos> for Board {
    type Output = Option<Piece>;

    // 根据位置获取棋子类型
    fn index(&self, pos: Pos) -> &Self::Output {
        &self.piece_array[pos]
    }
}

impl Board {
    // 根据棋子类型获取位棋盘
    pub fn get_bitboard(&self, color: Color, piece_type: PieceType) -> BitBoard {
        self.bitboard_array[color][piece_type]
    }

    pub fn get_side_bitboard(&self, side: Color) -> BitBoard {
        self.piece_bitboard_by_side[side]
    }

    pub fn get_both_side_bitboard(&self) -> BitBoard {
        self.piece_bitboard_by_side[Color::RED] | self.piece_bitboard_by_side[Color::BLACK]
    }

    pub fn side_to_move(&self) -> Color {
        self.side_to_move
    }
}
